#include <stdio.h>
#include "cpotimer.h"
#include "es_print.h"
#include "cpodata.h"
#include "cpoproc.h"
#include "cpofsm.h"
#include "cpolist.h"
#include "cpomqtt.h"

extern struct single_point syspoint_hd;
extern struct _subfsm_sn_t sf_sn;
extern cpo_fsm_head_l *cpo_subfsm_hd;
extern cpo_dev_t *cpo_dev;
extern cpo_fsm_t cpo_sysfsm;
extern struct mosquitto *mqtt_cpo;
extern struct _dk_repo dk_repo;
extern struct itimerval to_sigprop;

/* 定时器TMR_SYSPOINTS_ONWAIT超时后执行本函数
 * 检查系统点位的开关状态, 发送相关事件
 */
void cpo_to_sysp_onwait(void *arg)
{
    int flag = true;
    point_prop_t *pp;
    struct single_point *pos;

    ES_PRT_INFO("CPO: timer 'TMR_SYSPOINTS_ONWAIT' timeout, check syspoints' status... \n");
    
    pthread_mutex_lock(&syspoint_hd.lock);

    /* 空链表说明之前打开过的系统级的总阀的状态点位都不存在, 直接发送事件, 防止状态机阻塞在某一个状态 */
    // 我觉得实际工程这些状态点位是应该存在的，否则打开的点位不知道是否成功打开就直接执行后面的操作，这样做不合理
    if( list_empty(&syspoint_hd.slist) == 1 ){
        ES_PRT_INFO("CPO: Sys fsm: syspoints' state list is empty \n");
        send_event_2sysfsm(EV_SYSPOINTS_ON);
        pthread_mutex_unlock(&syspoint_hd.lock);
        return;
    }

    /* 根据状态链表中记录的点位的devicekey和function，
     * 从设备描述符中找到这些点位, 如果这些点位value都为true
     * 就发送事件
     */
    list_for_each_entry(pos, &syspoint_hd.slist, slist){

        pp = find_point_bydf(pos->pp.dev_key, pos->pp.func);
        if(pp == NULL || pp->val.b == false){
            ES_PRT_INFO("CPO: Sys fsm: syspoints' state are not enable, recheck on next timeout \n");
            pthread_mutex_unlock(&syspoint_hd.lock);
            return;
        }
    }

    /* 点位状态都为true, 就发送事件 EV_SYSPOINTS_ON 到系统状态机 */
    if(flag == true){
        ES_PRT_INFO("CPO: Sys fsm: syspoints' state are enable \n");
        send_event_2sysfsm(EV_SYSPOINTS_ON);
    }
    pthread_mutex_unlock(&syspoint_hd.lock);
    return;
}

void cpo_to_sysidle_stop(void *arg)
{
    ES_PRT_INFO("CPO: timer 'TMR_SYSIDLE_STOP' timeout \n");
    send_event_2sysfsm(EV_SYS_STOP);
}

void cpo_to_wp_onwait(void *arg)
{
    ES_PRT_INFO("CPO: timer 'TMR_WP_ONWAIT' timeout, check subfsm[%d] -- pchwp[%d] -- cwp[%d] status \n", \
                ((cpo_fsm_t *)arg)->fsmno, ((cpo_fsm_t *)arg)->bind_dev.pchwp_sn, ((cpo_fsm_t *)arg)->bind_dev.cwp_sn);

    cpo_fsm_t *subfsm_pos;
    /* 找到子状态机节点 */
    list_for_each_entry(subfsm_pos, &cpo_subfsm_hd->list, list){
        if(subfsm_pos->fsmno == ((cpo_fsm_t *)arg)->bind_dev.ch_sn){
            break;
        }
    }

    /* 空链表说明之前打开过的水阀的状态点位都不存在, 直接发送事件, 防止状态机阻塞在某一个状态 */
    // 我觉得实际工程这些状态点位是应该存在的，否则打开的点位不知道是否成功打开就直接执行后面的操作，这样做不合理
    if( list_empty(&subfsm_pos->wp_slist) == 1 ){
        ES_PRT_INFO("CPO: Sub fsm NO.[%d]: wp state list is empty \n", ((cpo_fsm_t *)arg)->bind_dev.ch_sn);
        send_event_2subfsm(EV_WP_ON, subfsm_pos->fsmno);
        return;
    }

    int flag = true;
    point_prop_t *pp;
    struct single_point *pos;

    /* 根据状态链表中记录的点位的devicekey和function，
     * 从设备描述符中找到这些点位, 如果这些点位value都为true
     * 就发送事件
     */
    list_for_each_entry(pos, &subfsm_pos->wp_slist, slist){

        pp = find_point_bydf(pos->pp.dev_key, pos->pp.func);
        if(pp == NULL || pp->val.b == false){
            ES_PRT_INFO("CPO: Sub fsm NO.[%d]: wp state are not enable, recheck on next timeout \n", ((cpo_fsm_t *)arg)->bind_dev.ch_sn);
            return;
        }
    }

    /* 点位状态都为true, 就发送事件 EV_SYSPOINTS_ON 到系统状态机 */
    if(flag == true){
        ES_PRT_INFO("CPO: Sub fsm NO.[%d]: wp state are enable \n", ((cpo_fsm_t *)arg)->bind_dev.ch_sn);
        send_event_2subfsm(EV_WP_ON, subfsm_pos->fsmno);
    }
    return;
}

void cpo_to_ch_onwait(void *arg)
{
    ES_PRT_INFO("CPO: timer 'TMR_CH_ONWAIT' timeout, check ch[%d] status \n", ((cpo_fsm_t *)arg)->bind_dev.ch_sn);

    ch_dev_t *ch_pos;
    point_prop_t *pp;

    struct timeval tv;
    gettimeofday(&tv, NULL);

    /* 根据子状态机((cpo_fsm_t *)arg)->bind_dev.ch_sn 记录的冷机号
     * 找到冷机设备描述符, 然后判断开关状态
     */
    list_for_each_entry(ch_pos, &cpo_dev->ch_head.list, list){
        if(ch_pos->sn == ((cpo_fsm_t *)arg)->bind_dev.ch_sn){
            pp = find_point_from_devdp_bynm("CH-S", &ch_pos->ch_prop);
            /* 没有状态点位 */
            if(pp == NULL){
                ES_PRT_INFO("CPO: Sub fsm NO.[%d]: ch has no CH-S when enable ch[%d] \n", \
                                            ((cpo_fsm_t *)arg)->bind_dev.ch_sn, ch_pos->sn);

                send_event_2subfsm(EV_CH_ON, ((cpo_fsm_t *)arg)->bind_dev.ch_sn);
                ch_pos->ch_prop.start_tm = tv.tv_sec;
            
            /* 有状态点位, 同时value为true */
            }else if(pp->val.b == true){
                ES_PRT_INFO("CPO: Sub fsm NO.[%d]: ch[%d] state is enable \n", \
                                            ((cpo_fsm_t *)arg)->bind_dev.ch_sn, ch_pos->sn);

                send_event_2subfsm(EV_CH_ON, ((cpo_fsm_t *)arg)->bind_dev.ch_sn);
                ch_pos->ch_prop.start_tm = tv.tv_sec;
            }
        }
    }
}

void cpo_to_ch_offwait(void *arg)
{
    ES_PRT_INFO("CPO: timer 'TMR_CH_OFFWAIT' timeout, check ch[%d] status \n", ((cpo_fsm_t *)arg)->bind_dev.ch_sn);

    struct timeval tv;
    gettimeofday(&tv, NULL);

    ch_dev_t *ch_pos;
    point_prop_t *pp;

    /* 根据子状态机((cpo_fsm_t *)arg)->bind_dev.ch_sn 记录的冷机号
     * 从cpo_dev->ch_head链表中找到冷机设备描述符, 然后判断开关状态
     */
    list_for_each_entry(ch_pos, &cpo_dev->ch_head.list, list){
        if(ch_pos->sn == ((cpo_fsm_t *)arg)->bind_dev.ch_sn){

            pp = find_point_from_devdp_bynm("CH-S", &ch_pos->ch_prop);

            if(pp == NULL){
                ES_PRT_INFO("CPO: Sub fsm NO.[%d]: ch has no CH-S when close ch[%d] \n", \
                                            ((cpo_fsm_t *)arg)->bind_dev.ch_sn, ch_pos->sn);

                send_event_2subfsm(((cpo_fsm_t *)arg)->fsmevt, ((cpo_fsm_t *)arg)->fsmno);
                ch_pos->ch_prop.down_tm = tv.tv_sec;
                ch_pos->ch_prop.is_run = false;
                ch_pos->ch_prop.curr_rt = 0;

            /* 有状态点位, 同时value为false */
            }else if(pp->val.b == false){
                ES_PRT_INFO("CPO: Sub fsm NO.[%d]: ch[%d] state is false \n", \
                                            ((cpo_fsm_t *)arg)->bind_dev.ch_sn, ch_pos->sn);

                send_event_2subfsm(((cpo_fsm_t *)arg)->fsmevt, ((cpo_fsm_t *)arg)->fsmno);
                ch_pos->ch_prop.down_tm = tv.tv_sec;
                ch_pos->ch_prop.is_run = false;
                ch_pos->ch_prop.curr_rt = 0;
            }
        }
    }
}

void cpo_ch_bf_close(void *arg)
{
    ES_PRT_INFO("CPO: timer 'TMR_CH_BF_CLOSE' timeout, send event[%d] \n", ((cpo_fsm_t *)arg)->fsmevt);
    send_event_2subfsm(((cpo_fsm_t *)arg)->fsmevt, ((cpo_fsm_t *)arg)->fsmno);
}

void cpo_to_wp_offwait(void *arg)
{
    ES_PRT_INFO("CPO: timer 'TMR_WP_OFFWAIT' timeout, check wp state \n");

    //TODO: 确认水泵已经关闭
    //TODO: 确认无水流，否则直接返回

    /* 1. clean up 水泵状态链表、命令链表 */
    data_type_u val;
    point_prop_t *pp;
    struct single_point *n;
    struct single_point *sp_pos;

    list_for_each_entry_safe(sp_pos, n, &((cpo_fsm_t *)arg)->wp_clist, clist){
        list_del(&sp_pos->clist);
        free(sp_pos);
    }

    list_for_each_entry_safe(sp_pos, n, &((cpo_fsm_t *)arg)->wp_slist, slist){
        list_del(&sp_pos->slist);
        free(sp_pos);
    }

    /* 2. close 冷却塔阀 */
    list_for_each_entry(sp_pos, &((cpo_fsm_t *)arg)->ct_clist, clist){
        pp = find_point_bydf(sp_pos->pp.dev_key, sp_pos->pp.func);
        if(pp != NULL){
            setval_2devdp_bypp(pp, val, TypeOfVal_BOOL);
            sendpoint_2otherapp(pp);
        }
    }

    /* clean up 子状态机下的冷却塔命令链表 */    
    list_for_each_entry_safe(sp_pos, n, &((cpo_fsm_t *)arg)->ct_clist, clist){
        list_del(&sp_pos->clist);
        free(sp_pos);
    }
    //TODO: clean up 冷却塔状态链表

    /* 3. close 冷机的几个阀 */
    val.b = false;

    list_for_each_entry(sp_pos, &((cpo_fsm_t *)arg)->v_clist, clist){
        pp = find_point_bydf(sp_pos->pp.dev_key, sp_pos->pp.func);
        if(pp != NULL){
            setval_2devdp_bypp(pp, val, TypeOfVal_BOOL);
            sendpoint_2otherapp(pp);
        }
    }

    /* clean up 子状态机下的冷机的几个阀的命令链表 */    
    list_for_each_entry_safe(sp_pos, n, &((cpo_fsm_t *)arg)->v_clist, clist){
        list_del(&sp_pos->clist);
        free(sp_pos);
    }
    //TODO: clean up 阀的状态链表

    ((cpo_fsm_t *)arg)->all_stop = true;

    send_event_2subfsm(((cpo_fsm_t *)arg)->fsmevt, ((cpo_fsm_t *)arg)->fsmno);
}

/* 如果系统完成停机操作，就完成如下内容：
    1. 释放状态机相关链表
    2. unsubscribe主题
    3. 删除设备描述符
    4. 复位相关变量
    (全局搜索malloc, 判断哪里需要free)

   如果没有完成停机操作：
    1. 重设定时器时间，等待下次操作
 */
void cpo_to_del_device(void *arg)
{
    /* 确认系统收到sys stop 后，完成停机操作，判断标准：系统状态机处于init状态 */
    //TODO:是否应该再判断冷机都处于关闭状态？
    if(cpo_sysfsm.cur_state != S_SYS_INIT){
        return;
    }

    /* 1. 清空子系统状态机链表 */
    cpo_fsm_t *pos, *n;
    list_for_each_entry_safe(pos, n, &cpo_subfsm_hd->list, list){
        ES_PRT_INFO("Del sub fsm[%d] \n", pos->fsmno);
        list_del(&pos->list);
        free(pos);
    }
    /* 2. unsubscribe topic 虚点 */
    char topic[128];
    sprintf(topic, "%s/%s/command", MQTT_SUB_VIRPOINT_PRE, cpo_dev->device_id);
    mosquitto_unsubscribe(mqtt_cpo, NULL, topic);

    int i;
    /* unsubscribe topic 实点 */
    for(i=0; i<dk_repo.num; i++){
        mosquitto_unsubscribe(mqtt_cpo, NULL, (char *)(dk_repo.ptr+i));
    }

    /* 清零操作很重要 */
    dk_repo.num = 0;
    free(dk_repo.ptr);
    dk_repo.ptr = NULL;

    /* 3. 删除设备描述符、点位属性描述符 */
    del_cpo_dev();

    pthread_cond_signal(&cpo_dev->deltmr_cond);
}

/* 等待delDevice完成，删除定时器 TMR_DELDEVICE */
void *cpo_deltmr_after_stop_thread(void *arg)
{
    pthread_mutex_lock(&cpo_dev->lock);

    pthread_cond_wait(&cpo_dev->deltmr_cond, &cpo_dev->lock);

    pthread_mutex_unlock(&cpo_dev->lock);

    es_timer_del(TMR_DELDEVICE);

    return NULL;
}